package com.huan.es8.suggesters;

import co.elastic.clients.elasticsearch._types.mapping.CompletionProperty;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionContext;
import com.huan.es8.AbstractEs8Api;
import org.junit.jupiter.api.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;

/**
 * <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.6/search-suggesters.html#completion-suggester">官方文档</a>
 * <pre>
 * 自动完成，只能实现 基于 前缀的 提示。
 * 1. mapping 的字段类型必须是 completion 类型。
 * 2. completion 类型的数据都是保存在内存中，使用时需要考虑清楚。
 * 3. 基于上下文的自动提示。
 * </pre>
 *
 * <pre>
 *     需求：
 *     假设系统中存在 2个菜单数据， 其中一个菜单 打上了 目录，系统菜单 的标志， 另外一个菜单 打上了 目录，菜单 的标识， 此时，当我们自动提示时，
 *     只需要打上了 菜单 的数据来提示。
 * </pre>
 *
 * @author huan.fu
 * @date 2023/1/28 - 20:24
 */
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ContextCompletionSuggesterApi extends AbstractEs8Api {

    @BeforeAll
    public void createIndex() throws IOException {
        client.indices()
                .create(indexRequest ->
                        indexRequest.index("test_completion")
                                .mappings(mappings ->
                                        mappings.properties("menu", new Property(
                                                new CompletionProperty.Builder()
                                                        .analyzer("ik_max_word")
                                                        .searchAnalyzer("ik_smart")
                                                        .contexts(context ->
                                                                context.name("menu-context")
                                                                        .type("category")
                                                        )
                                                        .build()))
                                )
                );
        bulk("test_completion", Arrays.asList(
                "{\"menu\": {\"input\":\"系统管理\", \"contexts\": {\"menu-context\": [\"目录\",\"系统菜单\"]}}}",
                "{\"menu\": {\"input\":\"系统管理->添加菜单\", \"contexts\": {\"menu-context\": [\"目录\",\"菜单\"]}}}"
        ));
    }

    @Test
    @DisplayName("基于上下文的自动提示")
    public void test01() throws IOException {
        SearchRequest request = SearchRequest.of(searchRequest ->
                // 从那个索引搜索
                searchRequest.index("test_completion")
                        .suggest(suggest ->
                                // 这次推荐的名字，随便写
                                suggest.suggesters("推荐的名字", suggester ->
                                        // 推荐的关键字，前缀匹配，如果设置了fuzziness，则可以模糊匹配，即 系m统 这样也可以搜索出来
                                        suggester.prefix("系统")
                                                // .regex("") // 也可以通过正则来实现 前缀 匹配
                                                // 自动完成
                                                .completion(completion ->
                                                        // 自动完成的字段，即从这个字段中返回
                                                        completion.field("menu")
                                                                // 返回多少条自动提示
                                                                .size(10)
                                                                // 是否跳过重复的自动提示
                                                                .skipDuplicates(true)
                                                                // 模糊搜索
                                                                .fuzzy(fuzzy -> fuzzy.fuzziness("auto"))
                                                                // 此处将 菜单 改成 目录 看自动提示的结果是否一致
                                                                .contexts("menu-context", Collections.singletonList(CompletionContext.of(context -> context.context(c -> c.category("菜单")))))
                                                )
                                )
                        )
        );

        System.out.println("request: " + request);
        SearchResponse<Object> response = client.search(request, Object.class);
        System.out.println("response: " + response);

    }

    @AfterAll
    public void deleteIndex() throws IOException {
        deleteIndex("test_completion");
    }

}
